#ifndef prs_Tree_H
#define prs_Tree_H

#include "svl.h"
#include "svl_TArrayFix.h"
#include "prs.h"
#include "svl_TVisitor.h"
#include "prs_AttrMessage.h"

namespace prs {

///////////////////////////////////////////////////////////////////////////////
extern const char *attrName_Musor;
extern const char *attrName_NodeType;
extern const char *attrName_Id;
extern const char *attrName_Null;
extern const char *attrName_OldChildren;
///////////////////////////////////////////////////////////////////////////////
struct TNode;
struct TNodeArray;
struct TNodeSearchResult;
struct TNodeSearch;
struct TNodeChildrenNodes;
struct IAttrPointerBaseArray;
struct TRedirectReferences;
///////////////////////////////////////////////////////////////////////////////
const int infoParamGroup_NodeType = 0;
const int infoParamGroup_Id = 1;
const int infoParamGroup_Default = 2;
const int infoParamGroup_Object = 3;
const int infoParamGroup_Child = 10;
///////////////////////////////////////////////////////////////////////////////

/*************
struct TDebug
{
  TDebug() { objectsCount++; }
 ~TDebug() { objectsCount--; }
  //
  static void Check();

private:
  struct TCheck
  {
   ~TCheck() { TDebug::Check();}
  };
  static int objectsCount;
  static TCheck check;
};
****************/

///////////////////////////////////////////////////////////////////////////////

struct TPossibleReferences : public svl::TArray
{
 ~TPossibleReferences() { svl::TArray::ClearNotDelete();}
  //
  int Count() { return svl::TArray::Count();}
  void Add(svl::TArray_Object *P);
  bool Search(svl::TArray_Object *P,int &OutN) { return svl::TArray::Search(P,OutN);}
  svl::TArray_Object *Get(int N) { return svl::TArray::Get(N);}

private:
  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////

struct IAttrNodeArrayBase 
{
  virtual char *GetAttrName()=0;
  virtual TNodeArray &GetValueAsNodeArray()=0;
};

///////////////////////////////////////////////////////////////////////////////

struct IAttrPointerBase
{
protected:
  virtual char *GetAttrName()=0;
  virtual svl::TPointer *GetValueAsPointer()=0;
  virtual void SetValueAsPointer(svl::TPointer *P)=0;
  //
  friend IAttrPointerBaseArray;
  friend TRedirectReferences;
};

///////////////////////////////////////////////////////////////////////////////

struct IAttrPointerBaseArray //: public svl::TDebug
{
  int Count() { return a.Count();}
  void AddAttr(IAttrPointerBase *Attr);
  IAttrPointerBase *Get(int N) { return a.Get(N);}//static_cast<TItem*>(a.Get(N))->attr;}

private:
/******
  struct TItem : public svl::TArray_Object
  {
    TItem(IAttrPointerBase *Attr) { attr = Attr;}
    IAttrPointerBase *attr;
  };  
  svl::TArray a;
********/  
  svl::TArrayPtrFix<IAttrPointerBase*,200> a;
};

///////////////////////////////////////////////////////////////////////////////

struct TRedirectReferences
{
  TPossibleReferences OldPR,NewPR;
  IAttrPointerBaseArray NewAttr;
  //
  void SetNewValues();
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeArray : public svl::TArray_Object //virtual public svl::TPointer
{
  TNodeArray() { owner = NULL;}
  //
  TNode *GetOwner()             { return owner;}
  void   SetOwner(TNode *Owner) { owner = Owner;}
  //
  int  Count() { return a.Count();}
  void Clear() { a.Clear();}
  //
  void SetEnDelete(TNode *Node,bool V);
  bool GetEnDelete(TNode *Node);
  //
  void Delete(int N);
  void Delete(TNode *Node);
  //
  void Add(TNode *Node,bool EnDelete);
  //
  void AddFirst (TNode *Add,bool EnDelete);
  void AddLast  (TNode *Add,bool EnDelete);
  void AddBefore(TNode *Add,TNode *Curr,bool EnDelete);
  void AddAfter (TNode *Add,TNode *Curr,bool EnDelete);
  //
  void Replace(int N,TNode *NewNode,bool EnDelete);
  //
  void MoveTo(TNodeArray &To);
  void UnlinkIfExsist(TNode *Node);
  //
  void NewAndCopyAttrTo(TNodeArray &To,bool Recursive = true);
  //
  TNode &Get(int N);
  int GetN(TNode *Node);
  TNode *GetFirst();
  TNode *GetLast();
  //
  // return Result
  TNodeArray &SearchByAttrName        (svl::TString AttrName,TNodeArray &Result,bool Recursive = false);
  TNodeArray &SearchByAttrNameAndValue(svl::TString AttrName,svl::TString Value,TNodeArray &Result,bool Recursive = false);
  TNodeArray &GotoChildren            (svl::TString AttrName,TNodeArray &Result,bool Recursive = false);
  TNodeArray &GotoChildren            (TNodeArray &Result);
  TNodeArray &SearchFirst             (TNodeArray &Result);
  //
  // return Result.curr
  TNodeArray &SearchByAttrName        (svl::TString AttrName,TNodeSearchResult &Result,bool Recursive = false);
  TNodeArray &SearchByAttrNameAndValue(svl::TString AttrName,svl::TString Value,TNodeSearchResult &Result,bool Recursive = false);
  TNodeArray &SearchFirst             (TNodeSearchResult &Result);
  //
  void Accept(svl::IBaseVisitor &Visitor);
  //
  virtual svl::TInfoParam AddInfoTo(svl::TInfoParam Info);
  
private:
  TNodeArray &searchByAttrName        (svl::TString &AttrName,TNodeArray &Result,bool Recursive);
  TNodeArray &searchByAttrNameAndValue(svl::TString &AttrName,svl::TString &Value,TNodeArray &Result,bool Recursive);
  TNodeArray &gotoChildren            (svl::TString &AttrName,TNodeArray &Result,bool Recursive,bool Flag2=false);
  TNodeArray &gotoChildren            (TNodeArray &Result);
  //
  void newAndCopyAttrTo(TNodeArray &To,TRedirectReferences &RR,bool Recursive);
  //
  svl::TArray a;
  TNode *owner;
  friend TNode;
  friend TNodeChildrenNodes;
  friend TNodeSearch;
  friend TNodeSearchResult;
  friend TNodeChildrenNodes;
  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////

template<class T>struct TNodeArrayT : public TNodeArray
{
  void Delete(T *Node) { TNodeArray::Delete(Node);}
  //
  void Add(T *Node,bool EnDelete) { TNodeArray::Add(Node,EnDelete);}
  //
  void AddFirst (T *Add,bool EnDelete)             { TNodeArray::AddFirst (Add,EnDelete);}
  void AddLast  (T *Add,bool EnDelete)             { TNodeArray::AddLast  (Add,EnDelete);}
  void AddBefore(T *Add,TNode *Curr,bool EnDelete) { TNodeArray::AddBefore(Add,Curr,EnDelete);}
  void AddAfter (T *Add,TNode *Curr,bool EnDelete) { TNodeArray::AddAfter (Add,Curr,EnDelete);}
  //
  void Replace(int N,T *NewNode,bool EnDelete) { TNodeArray::Replace(N,NewNode,EnDelete);}
  //
  T &Get(int N)     { return static_cast<T&>(TNodeArray::Get(N));}
  int GetN(T *Node) { return TNodeArray::GetN(Node);}
  T *GetFirst()     { return static_cast<T*>(TNodeArray::GetFirst());}
  T *GetLast()      { return static_cast<T*>(TNodeArray::GetLast());}
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeChildrenArrays : public svl::TArrayPtrFix<IAttrNodeArrayBase*,30>
{
  void Accept(svl::IBaseVisitor &Visitor);
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeChildrenNodes : public TNodeArray
{
 ~TNodeChildrenNodes() { TNodeArray::a.ClearNotDelete();}
  void Add(TNode *Node) { TNodeArray::Add(Node,false);}
};

/************************
struct TNodeChildrenNodes : public svl::TArrayPtrFix<TNode*,30>
{
private:
  void searchByAttrName(TNodeArray &Array,svl::TString AttrName,TNodeArray &Result,bool Recursive) {
    Array.searchByAttrName(AttrName,Result,Recursive);
  }
  void searchByAttrNameAndValue(TNodeArray &Array,svl::TString AttrName,svl::TString Value,TNodeArray &Result,bool Recursive) {
    Array.searchByAttrNameAndValue(AttrName,Value,Result,Recursive);
  }
  //
  //void Accept(TNodeArray &Array,svl::IBaseVisitor &Visitor);
};
*************************/

///////////////////////////////////////////////////////////////////////////////

struct TNodeSearchResult : public TDebug
{
  TNodeSearchResult() { curr = &a1; }
 ~TNodeSearchResult() { a1.a.ClearNotDelete(); a2.a.ClearNotDelete();}
  
private:  
  TNodeArray &Next() { if (curr == &a1) { curr = &a2; } else { curr = &a1; } return *curr; }
  TNodeArray *curr,a1,a2; 
  friend TNodeArray;
  friend TNode;
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeSearch : virtual public svl::TPointer
{
  TNodeSearch();
  TNodeSearch(TNode      *Node);
  TNodeSearch(TNodeArray *NodeArray);
 ~TNodeSearch() { a1.a.ClearNotDelete(); a2.a.ClearNotDelete();}
  //
  void SetFrom(TNode *Node);
  //
  TNodeArray &GetResult() { return *curr;}
  //
  int Count()        { return curr->Count(); }        
  TNode &Get(int N)  { return curr->Get(N);}
  void Delete(int N) { curr->Delete(N);}
  void Clear()       { curr->a.ClearNotDelete();}
  
  ///////////////////////////////////////////////////////////////////////////////////////////////
  TNodeSearch &SearchFarParentByAttrNameAndValue      (svl::TString AttrName,svl::TString Value);
  TNodeSearch &SearchFarThisOrParentByAttrNameAndValue(svl::TString AttrName,svl::TString Value);
  //
  TNodeSearch &SearchFarParentByType(svl::TString NodeType) {
    return SearchFarParentByAttrNameAndValue(attrName_NodeType,NodeType);
  }
  TNodeSearch &SearchFarThisOrParentByType(svl::TString NodeType) {
    return SearchFarThisOrParentByAttrNameAndValue(attrName_NodeType,NodeType);
  }

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  TNodeSearch &SearchNearParentByAttrNameAndValue      (svl::TString AttrName,svl::TString Value,bool Recursive = false);
  TNodeSearch &SearchNearThisOrParentByAttrNameAndValue(svl::TString AttrName,svl::TString Value,bool Recursive = false);
  //
  TNodeSearch &SearchNearParentByType(svl::TString NodeType,bool Recursive = false) {
    return SearchNearParentByAttrNameAndValue(attrName_NodeType,NodeType,Recursive);
  }
  TNodeSearch &SearchNearThisOrParentByType(svl::TString NodeType,bool Recursive = false) {
    return SearchNearThisOrParentByAttrNameAndValue(attrName_NodeType,NodeType,Recursive);
  }
                               
  ///////////////////////////////////////////////////////////////////////////////////////////////////////
  TNodeSearch &SearchByAttrNameAndValue(svl::TString AttrName,svl::TString Value,bool Recursive = false);
  TNodeSearch &GotoChildren(svl::TString AttrName,bool Recursive = false);
  TNodeSearch & AddChildren(svl::TString AttrName,bool Recursive = false);
  TNodeSearch &GotoChildren();
  //
  TNodeSearch &SearchByType(svl::TString NodeType,bool Recursive = false) {
    return SearchByAttrNameAndValue(attrName_NodeType,NodeType,Recursive);
  }
  TNodeSearch &SearchById(svl::TString Id,bool Recursive = false) {
    return SearchByAttrNameAndValue(attrName_Id,Id,Recursive);
  }

  //////////////////////////////
  TNodeSearch &SearchChildren() { return GotoChildren();}
  TNodeSearch &SearchByAttrName(svl::TString AttrName,bool Recursive = false);
  TNodeSearch &SearchFirst();

  //////////////////
  TNode *GetFirst();

  //
  virtual svl::TInfoParam AddInfoTo(svl::TInfoParam Info);
  
private:  
  TNodeArray &Next() { if (curr == &a1) { curr = &a2; } else { curr = &a1; } return *curr; }
  TNodeArray *curr,a1,a2; 
  friend TNodeArray;
  friend TNode;
  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////

struct TNode : public TDebug, public svl::TArray_Object, public svl::IVisitable//, public IMessage
{
  static  svl::TString GetClassName(TNode &V) { return svl_ClassName(V);}
  virtual svl::TString GetClassName()         { return svl_ClassName(*this);}
  //
  TNode() { parent = NULL;}
  //
  virtual bool GetValueByAttrName         (svl::TString &AttrName,svl::TString &Value) { return false;}
  virtual bool GetValueNodeByAttrName     (svl::TString &AttrName,TNode       *&Value) { return false;}
  virtual bool GetValueNodeArrayByAttrName(svl::TString &AttrName,TNodeArray  *&Value) { return false;}
  //
  //void Clear();                
  //
  void   SetParent(TNode *V) { parent = V;}
  TNode *GetParent()         { return parent;}
  //
  void GetChildrenArrays(TNodeChildrenArrays &Arrays) { TNode_GetChildrenArrays(Arrays);}
  void GetChildrenNodes (TNodeChildrenNodes  &Nodes)  { TNode_GetChildrenNodes(Nodes);}

  //
  void NewAndCopyAttrTo(TNode &To,bool Recursive = true);
  
  //
  template<class NodeType> static NodeType *newCopyFrom(NodeType *From) {
    NodeType *NewCopy = static_cast<NodeType*>(From->New());
    From->NewAndCopyAttrTo(*NewCopy);
    return NewCopy;
  }
  
  //void MoveThisToParent(TNode *ToParent);
  //
  virtual TNode *New() { Except("New"); return NULL;}
  //
  virtual void CopyAttrFrom(const TNode *From) { Except("CopyAttrFrom");}
  void operator = (TNode &From) { Except("operator = (TNode &From)");}
  
  /////////////////////////////
  struct TAddPossibleReferences
  {
    TAddPossibleReferences(TPossibleReferences &PR) : pr(PR) {}
    void Add(svl::TArray_Object *P) { pr.Add(P);}
  private:
    TPossibleReferences &pr;
  };
  virtual void AddPossibleReferences(TAddPossibleReferences &Param) { Param.Add(this);}
  
  //////////////////////
  struct TAddAttrPointer
  {
    TAddAttrPointer(IAttrPointerBaseArray &A) : a(A) {}
    void Add(IAttrPointerBase *P) { a.AddAttr(P);}
  private:
    IAttrPointerBaseArray &a;
  };
  virtual void AddAttrPointer(TAddAttrPointer &Param) {}

protected:
  virtual void TNode_GetChildrenArrays(TNodeChildrenArrays &Arrays) {}
  virtual void TNode_GetChildrenNodes (TNodeChildrenNodes  &Nodes) {}
  
private:
  void newAndCopyAttrTo(TNode &To,TRedirectReferences &RR,bool Recursive);
  //
  TNode *parent;
  friend TNodeArray;
  svl_Except;
};

///////////////////////////////////////////////////////////////////////////////
}

#endif
